﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Windows;
using SyncList.Business.ValueObject;
using SyncList.Business;
using SyncList.Scan;

namespace SyncList.Scanner
{

    /// <summary>
    /// Responsable du Scan des fichiers
    /// </summary>
    class FileScanner
    {

        /// <summary>
        /// Calculateur de Hash
        /// </summary>
        private IHashService _hash;



        /// <summary>
        /// Scanner un dossier
        /// </summary>
        /// <param name="collection">Collection à remplir</param>
        /// <param name="maxCount">Nombre maximum à scanner</param>        
        /// <param name="filter">Filtres comme la taille mini pour prendre en compte le fichier</param>
        /// <param name="mustStop">Pas forcément propre, sémaphore indiquant si l'on souhaite nous voir arrêter le traitement</param>
        /// <param name="folders">Liste des dossiers</param>
        /// <param name="timeOut">Temps maxi pendant lequel travailler, après on quitte pour sauver</param>
        /// <param name="writer">PErmet d'écrire une ligne de commentaire</param>
        /// <returns></returns>        
        public int Scan(List<FileItem> collection, string[] folders, FileFilter filter, int maxCount, TimeSpan timeOut, ref bool mustStop, IWriter writer)
        {
            _hash = ServiceAccessor.HashService;

            string doublonsFolder = "'#DOUBLONS#";
            

            List<string> files = new List<string>();
            foreach (string folder in folders)
            {
                string[] folderFiles = Directory.GetFiles(folder, "*", SearchOption.AllDirectories);
                files.AddRange(folderFiles);
            }
            writer.WriteLine("Scan started : "+  files.Count.ToString() +" files");

            DateTime start = DateTime.Now; // Je ne compte pas le temps de scan des répertoires dans le TimeOut

            int count = 0;
            int fileIndex = 0;
            foreach (string fileName in files)
            {
                // - Arrêt utilisateur
                if (mustStop)
                    break;


                if (fileName.Contains( doublonsFolder)) // Ignorer le sous-dossier réservé aux doublons! Sinon on boucle !!!
                    continue;
               

                int i = fileName.LastIndexOf(@"\");
                string nameOnly = fileName.Substring(i + 1);
                string folderOnly = fileName.Substring(0, i);

                if (nameOnly.Equals("Thumbs.db")) continue;

                // - Ignorer les éléments déjà faits
                FileItem found = collection.FirstOrDefault(c => c.Name.Equals(nameOnly) && c.LocalFolder.Equals(folderOnly));
                if (found == null)
                {

                    FileItem def = _hash.ComputeFileHash(folderOnly, nameOnly, filter);
                    if (def != null)
                    {                        

                        // - Rechercher le Hash dans la collection (si un film a été renommé)
                        found = collection.FirstOrDefault(c => c.Hash.Equals(def.Hash));
                        if (found == null)
                        {
                            // - Ajouter un élément qui n'existe pas
                            collection.Add(def);
                        }
                        else
                        {
                            // - Vérifier la taille
                            if (found.Size != def.Size)
                                throw new NotSupportedException("ON DIRAIT QU'IL Y A UNE COLLISION SHA-1 !, DEUX FICHIERS DE TAILLE DIFFERENTE ONT LE MEME HASH ! " + found.Name + " et " + def.Name);

                            // - Ajouter à la collection de doublons et ignorer si l'ancien fichier existe encore
                            string previousFile = found.LocalFolder + @"\" + found.Name;
                            string newFile = fileName;
                            if (File.Exists(previousFile))
                            {
                                //MessageBox.Show("Il y a un doublons entre " + newFile + " et " + previousFile + ". Déplacement d'un des 2 fichiers dans #DOUBLONS");

                                int pos = fileName.LastIndexOf("\\");
                                if (pos < 0) throw new NotSupportedException("Chemin non définissable");

                                string doublons = fileName.Substring(0, pos) + doublonsFolder;

                                //string doublons = folder + @"\" + doublonsFolder;
                                if(!Directory.Exists(doublons))
                                    Directory.CreateDirectory(doublons);

                                string targetBase = doublons + @"\" + nameOnly;
                                string target = targetBase;

                                string extension = "";
                                int posExt = targetBase.LastIndexOf(".");
                                if(posExt >= 0)
                                {
                                    extension = targetBase.Substring(posExt);
                                    targetBase = targetBase.Substring(0, posExt);
                                }

                                int index = 1;
                                while (File.Exists(target))
                                {
                                    target = targetBase + "." + index.ToString() + extension;
                                    index++;
                                }

                                File.Move(newFile, target);

                                // - Ecrire avec le même nom un fichier texte qui explique d'où vient le doublon
                                WriteFile(target + ".txt", new string[] { newFile, previousFile, "Hash : " + def.Hash, "Size : " + def.Size.ToString() });
                             
                                writer.WriteLine(" =====> DOUBLON DETECTE : " + newFile);
                            }
                            else
                            {
                                // - Remplacer un élément par un autre
                                int index = collection.IndexOf(found);  // Position de l'élément à remplacer
                                collection.Remove(found); // Enlever l'ancien
                                collection.Insert(index, def); // Ajouter le nouveau
                            }
                        }

                        //writer.WriteLine(def.Name + " Size:" + def.Size.ToString() + " Hash: " + def.Hash);


                        // --- Conditions d'arrêt

                        // - Nombre de max de fichiers en une fois
                        count++;
                        if (maxCount > 0)
                        {
                            maxCount--;
                            if (maxCount == 0) break;
                        }

                        // - Temps en une fois
                        if (timeOut.Ticks > 0)
                        {
                            if (DateTime.Now.Ticks > (start + timeOut).Ticks)
                                break;
                        }


                    }

                    
                }
                fileIndex++;
            }
            writer.WriteLine(DateTime.Now.ToString() + " ====> Save pause at file " + fileIndex.ToString() + " / " + files.Count.ToString());

            return (count);
        }



        /// <summary>
        /// Ecrire un fichier texte
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="lines"></param>
        private void WriteFile(string fileName, string[] lines)
        {
            using (FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write))
            {
                using (StreamWriter writer = new StreamWriter(file, Encoding.UTF8))
                {
                    foreach(string line in lines)
                        writer.WriteLine(line);
                }
            }

        }


      

    }
}
